package com.xxx.mylibrary.util

import android.content.Context
import android.content.SharedPreferences
import android.text.TextUtils
import com.google.gson.Gson
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import java.util.*

/**
 * SpUtils
 *
 * @author ZhongDaFeng
 */
class SpUtils private constructor(context: Context, spName: String, mode: Int) {
    private val sp: SharedPreferences

    /*string*/
    fun put(key: String, value: String?) {
        sp.edit().putString(key, value).apply()
    }

    fun getString(key: String): String? {
        return getString(key, "")
    }

    fun getString(key: String, defaultValue: String?): String? {
        return sp.getString(key, defaultValue)
    }

    /*int*/
    fun put(key: String, value: Int) {
        sp.edit().putInt(key, value).apply()
    }

    fun getInt(key: String): Int {
        return getInt(key, -1)
    }

    fun getInt(key: String, defaultValue: Int): Int {
        return sp.getInt(key, defaultValue)
    }

    /*long*/
    fun put(key: String, value: Long) {
        sp.edit().putLong(key, value).apply()
    }

    fun getLong(key: String): Long {
        return getLong(key, -1L)
    }

    fun getLong(key: String, defaultValue: Long): Long {
        return sp.getLong(key, defaultValue)
    }

    /*float*/
    fun put(key: String, value: Float) {
        sp.edit().putFloat(key, value).apply()
    }

    fun getFloat(key: String): Float {
        return getFloat(key, -1f)
    }

    fun getFloat(key: String, defaultValue: Float): Float {
        return sp.getFloat(key, defaultValue)
    }

    /*boolean*/
    fun put(key: String, value: Boolean) {
        sp.edit().putBoolean(key, value).apply()
    }

    fun getBoolean(key: String): Boolean {
        return getBoolean(key, false)
    }

    fun getBoolean(key: String, defaultValue: Boolean): Boolean {
        return sp.getBoolean(key, defaultValue)
    }

    /*map*/
    fun put(map: Map<String?, Any?>) {
        val edit = sp.edit()
        for ((key, value) in map) {
            if (value == null) continue
            if (value is String) {
                edit.putString(key, value.toString())
            } else if (value is Boolean) {
                edit.putBoolean(key, (value as Boolean?)!!)
            } else if (value is Int) {
                edit.putInt(key, (value as Int?)!!)
            } else if (value is Float) {
                edit.putFloat(key, (value as Float?)!!)
            } else if (value is Long) {
                edit.putLong(key, (value as Long?)!!)
            } else {
                throw UnsupportedOperationException("parameter Unsupported type!")
            }
        }
        edit.apply()
    }

    val all: Map<String, *>
        get() = sp.all

    /*Set<String>*/
    fun put(key: String, value: Set<String?>?) {
        sp.edit().putStringSet(key, value).apply()
    }

    fun getStringSet(key: String): Set<String>? {
        return getStringSet(key, emptySet<String>())
    }

    fun getStringSet(key: String, defaultValue: Set<String?>?): Set<String>? {
        return sp.getStringSet(key, defaultValue)
    }

    /*list*/
    fun <T> put(key: String?, list: List<T>?) {
        val gson = Gson()
        //转换成json数据，再保存
        val strJson: String = gson.toJson(list)
        sp.edit().putString(key, strJson).apply()
    }

    fun <T> getList(key: String?, cls: Class<T>?): ArrayList<T> {
        val list = ArrayList<T>()
        val strJson = sp.getString(key, null)
        if (TextUtils.isEmpty(strJson)) return list
        val gson = Gson()
        val array: JsonArray = JsonParser().parse(strJson).getAsJsonArray()
        for (jsonElement in array) {
            list.add(gson.fromJson(jsonElement, cls))
        }
        return list
    }

    /*Object*/
    fun <T> put(key: String?, `object`: Any?) {
        val gson = Gson()
        //转换成json数据，再保存
        val strJson: String = gson.toJson(`object`)
        sp.edit().putString(key, strJson).apply()
    }

    fun <T> getObject(key: String?, cls: Class<T>?): T? {
        var t: T? = null
        val strJson = sp.getString(key, "")
        if (TextUtils.isEmpty(strJson)) return t
        val gson = Gson()
        val `object`: JsonObject = JsonParser().parse(strJson).getAsJsonObject()
        t = gson.fromJson(`object`, cls)
        return t
    }

    operator fun contains(key: String): Boolean {
        return sp.contains(key)
    }

    fun remove(key: String) {
        sp.edit().remove(key).apply()
    }

    fun clear() {
        sp.edit().clear().apply()
    }

    companion object {
        private val SP_UTILS_MAP: MutableMap<String, SpUtils> = HashMap()
        fun getInstance(context: Context?, spName: String): SpUtils? {
            return getInstance(context, spName, Context.MODE_PRIVATE)
        }

        fun getInstance(context: Context?, spName: String, mode: Int): SpUtils? {
            var spName = spName
            if (context == null) {
                throw UnsupportedOperationException("context can't empty, please init me in SpHelpUtils.class")
            }
            if (isSpace(spName)) {
                spName = context.packageName + "_preferences"
            }
            var spUtils = SP_UTILS_MAP[spName]
            if (spUtils == null) {
                synchronized(SpUtils::class.java) {
                    spUtils = SP_UTILS_MAP[spName]
                    if (spUtils == null) {
                        spUtils = SpUtils(context, spName, mode)
                        SP_UTILS_MAP[spName] = spUtils!!
                    }
                }
            }
            return spUtils
        }

        private fun isSpace(s: String?): Boolean {
            if (s == null) {
                return true
            }
            var i = 0
            val len = s.length
            while (i < len) {
                if (!Character.isWhitespace(s[i])) {
                    return false
                }
                ++i
            }
            return true
        }
    }

    init {
        sp = context.getSharedPreferences(spName, mode)
    }
}